<html>
  <head>
    <meta charset="utf-8">
    <title>Learn3D</title>
    <link rel="stylesheet" href="public/webgl.css" type="text/css">
  </head>
  <script src="public/gl-matrix.js"></script>
  <script src="pbr.js"></script>
  <style type="text/css">
      div#canvas-frame {
          border: none;
          cursor: pointer;
          width: 512px;
          height: 512px;
          background-color: rgb(238, 238, 238);
      }
      div#horizontal {
          display: flex;
      }
  </style>

  <body onload="main();" ondblclick="end();" onkeypress="onKeyPress(event)" >
    <canvas id="glcanvas" width="512" height="512"></canvas>
    <button style="display:block; margin:0; width: 200px; height: 30px" onclick="updateShader()">编译并运行下面着色程序</button>
    <div id="vertical">
        <div id="horizontal" style="width: 90em;">
            <div id="vertical">
                <b>VertexShader</b>
                <textarea spellcheck="false" rows="33" cols="95" id="id_vertex_shader">
attribute vec4 aPosition;
attribute vec4 aNormal;

uniform mat4 uModelMatrix;
uniform mat4 uMITMatrix;
uniform mat4 uViewMatrix;
uniform mat4 uProjectionMatrix;

varying vec3 vNormal;
varying vec3 vPosition;

void main() {
    vPosition = vec3(aPosition.xyz) / aPosition.w;
    vNormal = normalize(vec3(uMITMatrix * vec4(aNormal.xyz, 0.0)));

    vec4 pntPos = uProjectionMatrix * uViewMatrix * uModelMatrix * aPosition;
    gl_Position = pntPos;
}
                </textarea>
            </div>
            <div id="vertical">
                <b>FragmentShader</b>
                <textarea spellcheck="false" rows="33" cols="95" id="id_fragment_shader">
precision mediump float;

varying vec3        vNormal;                // 法线
varying vec3        vPosition;              // 位置

uniform vec3        uLightPosition;         // 点灯光位置
uniform vec3        uLightColor;            // 点灯光颜色
uniform float       uLightRadius;           // 点灯光半径
uniform vec3        uCamPosition;           // 摄像机位置

uniform vec3        uBaseColor;             // 基础色
uniform vec3        uAlbedo;                // 反色率
uniform float       uRoughness;             // 粗糙度
uniform float       uMetallic;              // 金属
uniform float       uAmbientComponent;

#define PI          3.14159265359
#define invPI       0.3183098861837697
#define invTWO_PI   0.15915494309
#define saturate(x) clamp(x, 0.0, 1.0)

// specular components

// fresnel/specular fraction (or the fraction of light that gets reflected)
vec3 fresnelSchlick(float cosTheta, vec3 F0) {
    return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}

// normal distribution, depends on roughness of the material.
// also, proportional to the alignment of microfacets to the half vector
float DistributionGGX(vec3 N, vec3 H, float roughness) {
    float a1     = roughness * roughness;
    float a2     = a1 * a1;
    float NdotH  = max(dot(N, H), 0.0);
    float NdotH2 = NdotH * NdotH;

    float num   = a2;
    float denom = (NdotH2 * (a2 - 1.0) + 1.0);
    denom = PI * denom * denom;

    return num / denom;
}

// self occlusion due to the roughness of the material
float GeometrySchlickGGX(float NdotV,float roughness) {
    float r = (roughness + 1.0);
    float k = (r*r) / 8.0;

    float num   = NdotV;
    float denom = NdotV * (1.0 - k) + k;

    return num / denom;
}

float GeometrySmith(vec3 N,vec3 V, vec3 L, float roughness) {
    float NdotV = max(dot(N, V), 0.0);
    float NdotL = max(dot(N, L), 0.0);
    float ggx2  = GeometrySchlickGGX(NdotV, roughness);
    float ggx1  = GeometrySchlickGGX(NdotL, roughness);

    return ggx1 * ggx2;
}

void main() {
    vec3 N = normalize(vNormal);
    vec3 V = normalize(uCamPosition - vPosition);

    vec3 Lo = vec3(0.0);

    // iterate for multiple lights
    // for(int i = 0; i < 1; ++i)
    // {
    vec3 L = normalize(uLightPosition - vPosition);
    vec3 H = normalize(V + L);
    float distance    = length(uLightPosition - vPosition);
    float attenuation = 1.0 / (distance * distance);
    vec3 radiance     = uLightColor * attenuation;

    // find three components responsible for specular output

    // fresnel
    // for dielectrics low value of base refelectivity
    vec3 F0 = vec3(0.04);
    F0      = mix(F0, uAlbedo, uMetallic);
    vec3 F  = fresnelSchlick(max(dot(H, V), 0.0), F0);

    // normal distribution
    float NDF = DistributionGGX(N, H, uRoughness);

    //geometric occlusion
    float G   = GeometrySmith(N, V, L, uRoughness);

    vec3 numerator    = NDF * G * F;
    float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0);
    vec3 specular     = numerator / max(denominator, 0.001);

    vec3 kS = F;
    vec3 kD = vec3(1.0) - kS;

    kD *= 1.0 - uMetallic;

    float NdotL = max(dot(N, L), 0.0);
    Lo = (kD * uAlbedo / PI + specular) * radiance * NdotL;
    // }

    vec3 ambient = 0.03 * uAlbedo * uAmbientComponent;
    vec3 color = ambient + Lo;

    color = color / (color + vec3(1.0));
    color = pow(color, vec3(1.0/2.2));

    gl_FragColor = vec4(color, 1.0);
}
                </textarea>
            </div>
        </div><br>
    </div>
  </body>

</html>
